// Geometric Tools, Inc.
// http://www.geometrictools.com
// Copyright (c) 1998-2006.  All Rights Reserved
//
// The Wild Magic Library (WM3) source code is supplied under the terms of
// the license agreement
//     http://www.geometrictools.com/License/WildMagic3License.pdf
// and may not be copied or disclosed except in accordance with the terms
// of that agreement.

#include "Wm3FoundationPCH.h"
#include "Wm3ClodMesh.h"
using namespace Wm3;

WM3_IMPLEMENT_RTTI(Wm3,ClodMesh,TriMesh);
WM3_IMPLEMENT_STREAM(ClodMesh);
WM3_IMPLEMENT_DEFAULT_NAME_ID(ClodMesh,TriMesh);

//----------------------------------------------------------------------------
ClodMesh::ClodMesh (Vector3fArray* pkVertices, const IntArray* pkIndices,
    CollapseRecordArray* pkRecordArray, bool bGenerateNormals,
    bool bCachedNormals)
    :
    TriMesh(pkVertices,0,false,false)  // defer normal construction
{
    assert(pkRecordArray);

    m_iTargetRecord = 0;
    m_iCurrentRecord = 0;
    m_spkRecordArray = pkRecordArray;

    // Make a copy of the indices.
    int iIQuantity = pkIndices->GetQuantity();
    const int* aiIndex = pkIndices->GetData();
    int* aiCopyIndex = WM3_NEW int[iIQuantity];
    Indices = WM3_NEW IntArray(iIQuantity,aiCopyIndex);
    size_t uiSize = iIQuantity*sizeof(int);
    System::Memcpy(aiCopyIndex,uiSize,aiIndex,uiSize);

    if (bGenerateNormals)
    {
        GenerateNormals(bCachedNormals);
    }
}
//----------------------------------------------------------------------------
ClodMesh::ClodMesh ()
{
    m_iTargetRecord = 0;
    m_iCurrentRecord = 0;
}
//----------------------------------------------------------------------------
ClodMesh::~ClodMesh ()
{
}
//----------------------------------------------------------------------------
void ClodMesh::SelectLevelOfDetail ()
{
    const CollapseRecord* akRecord = m_spkRecordArray->GetData();

    // Get target record.  The virtual function may be overridden by a
    // derived class to obtain a desired automated change in the target.
    int iTargetRecord = GetAutomatedTargetRecord();

    // collapse mesh (if necessary)
    int* aiIndices = Indices->GetData();
    int i, iC;
    while (m_iCurrentRecord < iTargetRecord)
    {
        m_iCurrentRecord++;

        // replace indices in connectivity array
        const CollapseRecord& rkRecord = akRecord[m_iCurrentRecord];
        for (i = 0; i < rkRecord.IQuantity; i++)
        {
            iC = rkRecord.Indices[i];
            assert(aiIndices[iC] == rkRecord.VThrow);
            aiIndices[iC] = rkRecord.VKeep;
        }

        // reduce vertex count (vertices are properly ordered)
        Vertices->SetActiveQuantity(rkRecord.VQuantity);

        // reduce triangle count (triangles are properly ordered)
        Indices->SetActiveQuantity(3*rkRecord.TQuantity);
    }

    // expand mesh (if necessary)
    while (m_iCurrentRecord > iTargetRecord)
    {
        // restore indices in connectivity array
        const CollapseRecord& rkRecord = akRecord[m_iCurrentRecord];
        for (i = 0; i < rkRecord.IQuantity; i++)
        {
            iC = rkRecord.Indices[i];
            assert( aiIndices[iC] == rkRecord.VKeep );
            aiIndices[iC] = rkRecord.VThrow;
        }

        m_iCurrentRecord--;
        const CollapseRecord& rkPrevRecord = akRecord[m_iCurrentRecord];

        // increase vertex count (vertices are properly ordered)
        Vertices->SetActiveQuantity(rkPrevRecord.VQuantity);

        // increase triangle count (triangles are properly ordered)
        Indices->SetActiveQuantity(3*rkPrevRecord.TQuantity);
    }
}
//----------------------------------------------------------------------------
void ClodMesh::Draw (Renderer& rkRenderer, bool bNoCull)
{
    SelectLevelOfDetail();
    TriMesh::Draw(rkRenderer,bNoCull);
}
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
// streaming
//----------------------------------------------------------------------------
void ClodMesh::Load (Stream& rkStream, Stream::Link* pkLink)
{
    WM3_BEGIN_DEBUG_STREAM_LOAD;

    TriMesh::Load(rkStream,pkLink);

    // native data
    rkStream.Read(m_iCurrentRecord);
    rkStream.Read(m_iTargetRecord);

    if (rkStream.GetVersion() < StreamVersion(3,1))
    {
        // native data
        int iRecordQuantity;
        rkStream.Read(iRecordQuantity);
        if (iRecordQuantity)
        {
            CollapseRecord* akRecord =
                WM3_NEW CollapseRecord[iRecordQuantity];
            for (int i = 0; i < iRecordQuantity; i++)
            {
                akRecord[i].Read(rkStream);
            }
            m_spkRecordArray = WM3_NEW CollapseRecordArray(iRecordQuantity,
                akRecord);
        }
        else
        {
            m_spkRecordArray = 0;
        }
    }
    else
    {
        // link data
        Object* pkObject;
        rkStream.Read(pkObject); // m_spkRecordArray
        pkLink->Add(pkObject);
    }

    WM3_END_DEBUG_STREAM_LOAD(ClodMesh);
}
//----------------------------------------------------------------------------
void ClodMesh::Link (Stream& rkStream, Stream::Link* pkLink)
{
    TriMesh::Link(rkStream,pkLink);

    if (rkStream.GetVersion() >= StreamVersion(3,1))
    {
        Object* pkLinkID = pkLink->GetLinkID();
        m_spkRecordArray =
            (CollapseRecordArray*)rkStream.GetFromMap(pkLinkID);
    }
}
//----------------------------------------------------------------------------
bool ClodMesh::Register (Stream& rkStream) const
{
    if (!TriMesh::Register(rkStream))
    {
        return false;
    }

    if (m_spkRecordArray)
    {
        m_spkRecordArray->Register(rkStream);
    }
    return true;
}
//----------------------------------------------------------------------------
void ClodMesh::Save (Stream& rkStream) const
{
    WM3_BEGIN_DEBUG_STREAM_SAVE;

    TriMesh::Save(rkStream);

    // native data
    rkStream.Write(m_iCurrentRecord);
    rkStream.Write(m_iTargetRecord);

    // link data
    rkStream.Write(m_spkRecordArray);

    WM3_END_DEBUG_STREAM_SAVE(ClodMesh);
}
//----------------------------------------------------------------------------
StringTree* ClodMesh::SaveStrings (const char*)
{
    int iCQuantity = (m_spkRecordArray? 2 : 1);
    StringTree* pkTree = WM3_NEW StringTree(3,iCQuantity);

    // strings
    pkTree->SetString(0,Format(&TYPE,GetName()));
    pkTree->SetString(1,Format("current record =",m_iCurrentRecord));
    pkTree->SetString(2,Format("target record =",m_iTargetRecord));

    // children
    pkTree->SetChild(0,TriMesh::SaveStrings());
    if (m_spkRecordArray)
    {
        pkTree->SetChild(1,m_spkRecordArray->SaveStrings());
    }

    return pkTree;
}
//----------------------------------------------------------------------------
int ClodMesh::GetMemoryUsed () const
{
    int iSize = sizeof(ClodMesh) - sizeof(TriMesh) +
        TriMesh::GetMemoryUsed();

    int iRecordQuantity = m_spkRecordArray->GetQuantity();
    const CollapseRecord* akRecord = m_spkRecordArray->GetData();
    for (int i = 0; i < iRecordQuantity; i++)
    {
        iSize += akRecord[i].GetMemoryUsed();
    }

    return iSize;
}
//----------------------------------------------------------------------------
int ClodMesh::GetDiskUsed (const StreamVersion& rkVersion) const
{
    int iSize = TriMesh::GetDiskUsed(rkVersion) +
        sizeof(m_iCurrentRecord) +
        sizeof(m_iTargetRecord);

    if (rkVersion < StreamVersion(3,1))
    {
        int iRecordQuantity = m_spkRecordArray->GetQuantity();
        const CollapseRecord* akRecord = m_spkRecordArray->GetData();
        iSize += sizeof(iRecordQuantity);
        for (int i = 0; i < iRecordQuantity; i++)
        {
            iSize += akRecord[i].GetDiskUsed();
        }
    }
    else
    {
        iSize += sizeof(m_spkRecordArray);
    }

    return iSize;
}
//----------------------------------------------------------------------------
